#include "http.h"

int epfd = 0;  // epoll树根

// 读取文件中的所有内容
char * read_file_all(FILE* file);
// 根据状态和信息构建http的返回内容并发送
void http_back(int status, const char* info, int sock);
// http路由，根据特定的路由规则选择不同的处理方式
void router(const char * path, int sock);
// 解析客户端发送的http请求
void * request_parse(int sock);
// 打印错误信息并退出
void error_die(const char * info);
// 根据args的参数构建http监听服务
int start_server(const httpd_args* args);
// 程序主循环
void main_loop(int httpd);

int start_server(const httpd_args* args){
	int httpd = 0, option;
	struct sockaddr_in http_addr;
    memset(&http_addr, 0, sizeof(http_addr));
	//创建socket
	httpd = socket(PF_INET, SOCK_STREAM, 0);
	if (httpd == -1)
	    error_die("socket");
	
	socklen_t optlen;
	optlen = sizeof(option);
    option = 1;
    // 设置地址复用
    setsockopt(httpd, SOL_SOCKET, SO_REUSEADDR, (void *)&option, optlen);
	http_addr.sin_family = AF_INET;
	http_addr.sin_port = htons(args->port);
	http_addr.sin_addr.s_addr = htonl(INADDR_ANY);

	if(bind(httpd, (struct sockaddr *)&http_addr, sizeof(http_addr)) < 0)
		error_die("bind");

	if(listen(httpd, args->max_conn) < 0)
		error_die("listen");

    // 将socket端口加入到epoll的监听中去
    struct epoll_event epoll_ev;
    epoll_ev.events = EPOLLIN;
    epoll_ev.data.fd = httpd;
    // init a epoll tree
    if((epfd = epoll_create1(0)) == -1) error_die("epoll_create"); 
    // printf("%d: epfd, %d: httpd\n", epfd, httpd); 
    if(epoll_ctl(epfd, EPOLL_CTL_ADD, httpd, &epoll_ev) == -1) error_die("epoll_ctl");

	return(httpd);
}


int read_char(int fd)
{
    ssize_t read_len;
    char buf[1];
    read_len = read(fd, buf, 1);
    if(read_len == 1){
        return buf[0];
    }else{
        return read_len;
    }
}

int read_skip_blank_char(int fd)
{
    char c = read_char(fd);
    while(c == '\t' || c == ' ') c = read_char(fd);

    return c;
}

int read_num(int fd, int num)
{
    if(num == 0) return 0;
    int total = 0;
    int read_len = 0;
    char buf[4096];
    memset(buf, 0, sizeof(buf));
    do{
        read_len = read(fd, buf, 1);
        total += read_len;
    }while(total < num && read_len > 0);
    return total;
}

// 解析socket发送的请求
void * request_parse(int sock){
    char path[1024], method[7], protocol[10];
    char key[1024], val[1024];
    int content_size = 0;
    char cur = 0;
    int i = 0;
    memset(path, 0, sizeof(path));
    memset(method, 0, sizeof(method));
    memset(protocol, 0, sizeof(protocol));
    memset(key, 0, sizeof(key));
    memset(val, 0, sizeof(val));
    do{
        cur = read_char(sock);
    }while(cur == ' ' || cur == '\t' || cur == '\r' || cur == '\n');
    if(cur == 0)
    {
        // 客户端断开连接
        epoll_ctl(epfd, EPOLL_CTL_DEL, sock, NULL);
        close(sock);
        printf("client is disconnect \n");
        return NULL;
    }
    // 读取http请求方法
    i = 0;
    while(cur != ' ' && cur != '\t' && i < 6){
        printf("%d", cur);
        method[i ++] = cur;
        cur = read_char(sock);
    }
    printf("\n");
    printf("i : %d\n", i);
    if(i >= 6) {printf("Invalid http request"); return NULL;};
    if(strcasecmp(method, "GET") && strcasecmp(method, "POST")) {
        printf("The method is not support in this website"); 
        return NULL;
    }
    printf("method : %s\n", method);
    
    // 读取请求地址
    do{
        cur = read_char(sock);
    }while(cur == ' ' || cur == '\t');
    i = 0;
    while(cur != ' ' && cur != '\t'){
        path[i ++] = cur;
        cur = read_char(sock);
    }
    printf("url : %s\n", path);

    // 读取协议
    cur = read_skip_blank_char(sock);
    i = 0;
    while(cur != '\r'){
        protocol[i ++] = cur;
        cur = read_char(sock); 
    }
    read_char(sock);
    printf("proto : %s\n", protocol);

    // 读取各种参数
    while(1)
    {
        if( (cur = read_skip_blank_char(sock)) == '\r'){
            read_char(sock);
            // 结束了
            // 将数据部分也全部取走
            printf("here to get extra len : %d\n", content_size);
            if( content_size != read_num(sock, content_size)) error_die("Wrong when read the data");
            printf("what\n");
            break;
        }else {
            memset(key, 0, sizeof(key));
            memset(val, 0, sizeof(val));
            i = 0;
            do{
                key[i++] = cur;
                cur = read_skip_blank_char(sock);
            }while(cur != ':');
            i = 0;
            do{
                cur = read_skip_blank_char(sock);
                val[i++] = cur; 
            }while(cur != '\r');
            printf("key : %s, val : %s\n", key, val);
            if(!strcasecmp(key, "Content-Length")){
                content_size = atoi(val);
            }
            read_char(sock);
        }
    }
    // 根据请求地址去处理
    if(!strcasecmp(method, "GET")){
        router(path, sock);
    }
    sleep(10);
    return NULL;
}

// 路由，转发请求
void router(const char * path, int sock)
{
    FILE * file;
    // 防止恶意请求突破 www 文件夹的限制
    if(path[0] != '/') {
        http_back(404, "Invalid request address", sock);
        return ;
    }
    if(!strcmp(path, "/"))
    {
        file = fopen("www/index.html", "r");
        if(file == NULL){
            http_back(404, "Not Found", sock);
            return ;
        }
        char* buf = read_file_all(file);
        http_back(200, buf, sock);
        free(buf);
        fclose(file);
    }else{
        
        char new_path[1024];
        memset(new_path, 0, sizeof(new_path));
        strcpy(new_path, "www");
        strcat(new_path, path);
        printf("new_path: %s\n", new_path);
        file = fopen(new_path, "r");
        if(file == NULL){
            http_back(404, "Not Found", sock);
            return ;
        }

        char* buf = read_file_all(file);
        http_back(200, buf, sock);
    
        free(buf);
        fclose(file);
    }
}

int write_num(int fd, const char* buf, int num)
{
    int total = 0;
    int write_len = 0;
    do{
        write_len = write(fd, buf, num - total);
        total += write_len;
    }
    while(total < num && write_len > 0);
    return total;
}

// 构建http返回信息
void http_back(int status, const char* info, int sock)
{
    const char * desc = NULL;
    switch (status)
    {
    case 200:
        desc = "OK";
        break;
    case 404:
        desc = "Not Found";
        break;
    default:
        desc = "Not Support";
        break;
    }
    char header[1024];
    memset(header, 0, sizeof(header));
    sprintf(header, "HTTP/1.1 %d %s\r\nContent-Type:text/html;charset=utf-8\r\nContent-Length:%lu\r\n\r\n", status, desc, strlen(info));
    
    // write(sock, info, sizeof(info));
    char *send_buf = (char *)malloc(sizeof(info) + sizeof(header) + 1);
    strcpy(send_buf, header);
    send_buf[sizeof(header)] = 0;
    strcat(send_buf, info);
    send_buf[sizeof(header)+sizeof(info)] = 0;
    printf("send_buf: %s\n", send_buf);
    int len = write_num(sock, send_buf, strlen(send_buf));
    free(send_buf);
}

// 读取文件所有的内容
// 返回的指针需要手动释放
char * read_file_all(FILE* file)
{
    fseek(file, 0,SEEK_END);
    ssize_t file_size = ftell(file);
    char * buf = (char *)malloc(sizeof(char) * file_size + 1);
    fseek(file, 0, SEEK_SET);
    fread(buf, file_size, 1, file);
    buf[file_size] = 0;
    return buf;
}

void main_loop(int httpd)
{
    struct epoll_event epoll_ev;
    struct epoll_event epollevents[128];
    struct sockaddr_in peer_sock;
    int psocklen = sizeof(peer_sock); 
    for(;;)
    {
        int ready_num;
        ready_num = epoll_wait(epfd, epollevents, 128, -1);
        if(ready_num == -1)
            error_die("epoll_wait");
        for(int i = 0; i < ready_num; i++)
        {
            if(epollevents[i].data.fd == httpd){
                int pfd = accept(httpd ,(struct sockaddr*)&peer_sock, &psocklen);
                if(pfd == -1) error_die("accept");
                char addr[20];
                memset(addr, 0, sizeof(addr));
                inet_ntop(AF_INET, (void *)&peer_sock.sin_addr,addr, 20);
                printf("The peer sock info: port: %d, addr: %s\n", ntohs(peer_sock.sin_port), addr);
                // 将相应的socket加入到epoll树中去
                epoll_ev.events = EPOLLIN;
                epoll_ev.data.fd = pfd;
                if(epoll_ctl(epfd, EPOLL_CTL_ADD, pfd, &epoll_ev) == -1) error_die("epoll_ctl");
            }else if(epollevents[i].events == EPOLLIN){
                // 某一套接字可读
                request_parse(epollevents->data.fd);
            }
        }
    }
}

int main()
{
    httpd_args server_opt;
    server_opt.port = 80;
    server_opt.max_conn = 128;
    int httpd = start_server(&server_opt);
    main_loop(httpd);
}

void error_die(const char * info)
{
    perror(info);
    exit(-1);
}